package platform.standard.audio;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.SourceDataLine;


import engine.Constants;
import engine.audio.AudioBuffer_I;

public class JavaxAudio implements AudioBuffer_I {
	public JavaxAudio(int bufferSize) {
		_Size = bufferSize;
		_Free = bufferSize;
		_ReadIndex = 0;
		_WriteIndex = 0;
		_Buffer = new byte[bufferSize];
		IsInitialized = initPlatform();
	}
	
	private boolean initPlatform() {
    	_Format = new AudioFormat(Constants.AUDIO_RATE, 
    								   Constants.AUDIO_BITS, 
    								   1, true, true);
		// Initialize a line from the sound system.
    	try {
    		_Line = AudioSystem.getSourceDataLine(_Format);
    		_Line.open();
    		_Line.start();
    	} catch (Exception e) {
    		System.out.println("Audio init failure.");
    		return false;
    	}
    	return true;
	}
	
	public int play() {
		
		int available = _Line.available();
		int sampleCount = _Size - _Free;
		if(sampleCount > available) {
			sampleCount = available;
		}
		
		if (sampleCount <= 0) {
			return 0;
		}
		
		int aLoop = _Size - _ReadIndex;
		if(aLoop > sampleCount) {
			aLoop = sampleCount;
		}
		
		int bLoop = sampleCount - aLoop;
		
		int rIndex = _ReadIndex;
		
		_Line.write(_Buffer, rIndex, aLoop);
		
		rIndex += aLoop;
		if(rIndex >= _Size) {
			rIndex = 0;
		}
		
		if(bLoop > 0) {
			_Line.write(_Buffer, rIndex, bLoop);
		}
		rIndex += bLoop;
		_ReadIndex = rIndex;
		_Free += sampleCount;
		
		return sampleCount;
	}
	
	public void shutdown() {
		// Get rid of our data line.
		if(_Line != null) {
			_Line.drain();
			_Line.close();
		}
	}
	
	public final boolean IsInitialized;
	private SourceDataLine _Line;
	private AudioFormat _Format;
	
	private final int _Size;
	private int _Free;
	private int _ReadIndex;
	private int _WriteIndex;
	private byte[] _Buffer;
	
	// AudioBuffer_I -----------------------------------------------------------
	
	@Override
	public int getSize() {
		return _Size;
	}

	@Override
	public int getFree() {
		return _Free;
	}

	@Override
	public void clear() {
		_Free = _Size;
		_ReadIndex = 0;
		_WriteIndex = 0;
	}

	@Override
	public int fill(byte[] inBuffer, int size) {
		// Sanitize
		if(inBuffer == null) {
			throw new NullPointerException(this.getClass().getName() + 
					".drain(null) argument.");
		}
			
		if(_Buffer == null) {
			return 0;
		}
		
		int sampleCount = size;	
		if(sampleCount >  _Free) {
			sampleCount = _Free;
		}
		int aLoop = _Size - _WriteIndex;
		if(aLoop > sampleCount) {
			aLoop = sampleCount;
		}
		
		int bLoop = sampleCount - aLoop;
		
		// Copy data. 
		int wIndex = _WriteIndex;
		int rIndex = 0;
		
		while(aLoop-- > 0) {
			_Buffer[wIndex++] = inBuffer[rIndex++];
		}
		
		if(wIndex >= _Size) {
			wIndex = 0;
		}
		
		while(bLoop-- > 0) {
			_Buffer[wIndex++] = inBuffer[rIndex++];
		}
		
		_WriteIndex = wIndex;
		_Free -= sampleCount;
		
		return sampleCount;	
	}

	@Override
	public int drain(byte[] outBuffer, int size) {
		// Sanitize
		if(outBuffer == null) {
			throw new NullPointerException(this.getClass().getName() + 
					".drain(null) argument.");
		}
			
		if(_Buffer == null) {
			return 0;
		}
		
		int sampleCount = size;	
		if(sampleCount > (_Size - _Free))
			sampleCount = _Size - _Free;
		
		int aLoop = _Size - _ReadIndex;
		if(aLoop > sampleCount) {
			aLoop = sampleCount;
		}
		
		int bLoop = sampleCount - aLoop;
		
		// Copy data. 
		int wIndex = 0;
		int rIndex = _ReadIndex;
		byte[] buffer = _Buffer;
		while(aLoop-- > 0) {
			outBuffer[wIndex++] = buffer[rIndex++];
		}
		
		if(rIndex >= _Size) {
			rIndex = 0;
		}
		
		while(bLoop-- > 0) {
			outBuffer[wIndex++] = buffer[rIndex++];
		}
		
		_ReadIndex = rIndex;
		_Free += sampleCount;
		
		return sampleCount;	
	}

	@Override
	public byte[] getData() {
		int sampleCount = _Size - _Free;
		if(sampleCount == 0) {
			return null;
		}
		byte[] outBuffer = new byte[sampleCount];
		
		int aLoop = _Size - _ReadIndex;
		if(aLoop > sampleCount) {
			aLoop = sampleCount;
		}
		
		int bLoop = sampleCount - aLoop;
		
		// Copy data. 
		int wIndex = 0;
		int rIndex = _ReadIndex;
		byte[] buffer = _Buffer;
		while(aLoop-- > 0) {
			outBuffer[wIndex++] = buffer[rIndex++];
		}
		
		if(rIndex >= _Size) {
			rIndex = 0;
		}
		
		while(bLoop-- > 0) {
			outBuffer[wIndex++] = buffer[rIndex++];
		}
		return outBuffer;
	}

}